﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BookClubIt.DTO;
using BookClubIt.Processor.AmazonAPI;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Xml;
using System.Configuration;

namespace BookClubIt.Processor
{
    public class AmazonProcessor
    {
        string accessKey, secretKey, associateTag;

        public AmazonProcessor()
        {
            accessKey = string.IsNullOrEmpty(ConfigurationManager.AppSettings["accessKey"]) ? "0E5SS5XF7KAR1BW56AR2" : ConfigurationManager.AppSettings["accessKey"];
            secretKey = string.IsNullOrEmpty(ConfigurationManager.AppSettings["secretKey"]) ? "bNpiUHZyXwQkYQeg2KF4hJolwGqjS48EPR4Uj5kT" : ConfigurationManager.AppSettings["secretKey"];
            associateTag = string.IsNullOrEmpty(ConfigurationManager.AppSettings["associateTag"]) ? "boo018-20" : ConfigurationManager.AppSettings["associateTag"];
        }

        public Books LookUpBook(Request request)
        {
            ItemLookupRequest lookUpRequest = request.ItemLookupRequest;
            ItemSearchRequest searchRequest = request.ItemSearchRequest;
            Items[] items;
            Books books = new Books();
            BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
            binding.MaxReceivedMessageSize = 50000000;
            binding.ReaderQuotas.MaxStringContentLength = 50000000;
            using (AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(
                binding,
                new EndpointAddress("https://webservices.amazon.com/onca/soap?Service=AWSECommerceService")))
            {
                client.ChannelFactory.Endpoint.Behaviors.Add((new AmazonSigningEndpointBehavior(accessKey, secretKey)));

                if (lookUpRequest != null)
                {
                    items = ProcessLookupResponse(client, lookUpRequest);
                }
                else if (searchRequest != null)
                {
                    items = ProcessSearchResponse(client, searchRequest);
                }
                else
                {
                    items = new Items[0];
                }
                int year;
                DateTime yearOnly;
                foreach (var item in items)
                {
                    if (item.Item != null)
                    {
                        foreach (var listItem in item.Item)
                        {
                            books.Add(new Book
                            {
                                Title = listItem.ItemAttributes.Title != null ? listItem.ItemAttributes.Title : string.Empty,
                                ASIN = listItem.ASIN != null ? listItem.ASIN : string.Empty,
                                EAN = listItem.ItemAttributes.EAN != null ? listItem.ItemAttributes.EAN : string.Empty,
                                ISBN = listItem.ItemAttributes.ISBN != null ? listItem.ItemAttributes.ISBN : string.Empty,
                                AmazonURL = listItem.DetailPageURL,
                                LargeImageURL = listItem.LargeImage != null ? listItem.LargeImage.URL : string.Empty,
                                MediumImageURL = listItem.MediumImage != null ? listItem.MediumImage.URL : string.Empty,
                                SmallImageURL = listItem.SmallImage != null ? listItem.SmallImage.URL : string.Empty,
                                Author = listItem.ItemAttributes.Author != null ? listItem.ItemAttributes.Author[0] : string.Empty,
                                Binding = listItem.ItemAttributes.Binding != null ? listItem.ItemAttributes.Binding : string.Empty,
                                Manufacturer = listItem.ItemAttributes.Manufacturer != null ? listItem.ItemAttributes.Manufacturer : string.Empty,
                                Pages = listItem.ItemAttributes.NumberOfPages != null ? listItem.ItemAttributes.NumberOfPages : string.Empty,
                                PublishedDate = int.TryParse(listItem.ItemAttributes.PublicationDate, out year) ? yearOnly = new DateTime(year, 1, 1) : Convert.ToDateTime(listItem.ItemAttributes.PublicationDate) != DateTime.MinValue ? Convert.ToDateTime(listItem.ItemAttributes.PublicationDate) : DateTime.MinValue,
                                Publisher = listItem.ItemAttributes.Publisher != null ? listItem.ItemAttributes.Publisher : string.Empty,
                                ReadingLevel = listItem.ItemAttributes.ReleaseDate != null ? listItem.ItemAttributes.SKU : string.Empty,
                                Editorial = listItem.EditorialReviews != null && listItem.EditorialReviews[0].Content != null ? listItem.EditorialReviews[0].Content : string.Empty
                            });
                        }
                    }
                }
                
            }

            return books;
        }

        private Items[] ProcessSearchResponse(AWSECommerceServicePortTypeClient client, ItemSearchRequest searchRequest)
        {
            if (searchRequest.ResponseGroup == null || searchRequest.ResponseGroup.Length == 0) searchRequest.ResponseGroup = new string[] { "Medium" };
            ItemSearch itemSearch = new ItemSearch();
            itemSearch.Request = new ItemSearchRequest[] { searchRequest };
            itemSearch.AWSAccessKeyId = accessKey;
            itemSearch.AssociateTag = accessKey;

            // issue the ItemSearch request
            ItemSearchResponse response = client.ItemSearch(itemSearch);
            return response.Items;
        }

        private Items[] ProcessLookupResponse(AWSECommerceServicePortTypeClient client, ItemLookupRequest lookUpRequest)
        {
            if (lookUpRequest.ResponseGroup == null || lookUpRequest.ResponseGroup.Length == 0) lookUpRequest.ResponseGroup = new string[] { "Medium" };

            //  ItemSearch itemSearch = new ItemSearch();
            ItemLookup itemLookup = new ItemLookup();
            itemLookup.Request = new ItemLookupRequest[] { lookUpRequest };
            itemLookup.AWSAccessKeyId = accessKey;
            itemLookup.AssociateTag = associateTag;

            // issue the ItemSearch request
            ItemLookupResponse response = client.ItemLookup(itemLookup);
            return response.Items;
        }

        public Books LookUpBookByASIN(string ASIN)
        {
            ItemLookupRequest request = new ItemLookupRequest();
            request.ItemId = new string[] { ASIN };
            request.ResponseGroup = new string[] { "Medium" };

            return LookUpBook(new Request { ItemLookupRequest = request });
        }

        public Books GetOffersByASIN(string ASIN)
        {
            ItemLookupRequest request = new ItemLookupRequest();
            request.ItemId = new string[] { ASIN };
            request.ResponseGroup = new string[] { "Offers" };

            return LookUpBook(new Request { ItemLookupRequest = request });
        }

        public Books SearchBookByTitle(string title)
        {
            return SearchBookByTitle(title, null);
        }

        public Books SearchBookByTitle(string title, string sortOrder)
        {
            ItemSearchRequest request = new ItemSearchRequest();
            request.SearchIndex = "Books";
            request.Sort = sortOrder;
            request.Keywords = title;
            request.ResponseGroup = new string[] { "Medium" };

            return LookUpBook(new Request { ItemSearchRequest = request });
        }


        public Books GetBookByASIN(string A_ASIN)
        {
            throw new NotImplementedException();
        }
    }


    public class AmazonHeader : MessageHeader
    {
        private string name;
        private string value;

        public AmazonHeader(string name, string value)
        {
            this.name = name;
            this.value = value;
        }

        public override string Name { get { return name; } }
        public override string Namespace { get { return "http://security.amazonaws.com/doc/2007-01-01/"; } }

        protected override void OnWriteHeaderContents(XmlDictionaryWriter xmlDictionaryWriter, MessageVersion messageVersion)
        {
            xmlDictionaryWriter.WriteString(value);
        }
    }


    public class AmazonSigningEndpointBehavior : IEndpointBehavior
    {
        private string accessKeyId = "";
        private string secretKey = "";

        public AmazonSigningEndpointBehavior(string accessKeyId, string secretKey)
        {
            this.accessKeyId = accessKeyId;
            this.secretKey = secretKey;
        }

        public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(new AmazonSigningMessageInspector(accessKeyId, secretKey));
        }

        public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { return; }
        public void Validate(ServiceEndpoint serviceEndpoint) { return; }
        public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters) { return; }
    }


    public class AmazonSigningMessageInspector : IClientMessageInspector
    {
        private string accessKeyId = "";
        private string secretKey = "";

        public AmazonSigningMessageInspector(string accessKeyId, string secretKey)
        {
            this.accessKeyId = accessKeyId;
            this.secretKey = secretKey;
        }

        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            // prepare the data to sign
            string operation = Regex.Match(request.Headers.Action, "[^/]+$").ToString();
            DateTime now = DateTime.UtcNow;
            string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");
            string signMe = operation + timestamp;
            byte[] bytesToSign = Encoding.UTF8.GetBytes(signMe);

            // sign the data
            byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
            HMAC hmacSha256 = new HMACSHA256(secretKeyBytes);
            byte[] hashBytes = hmacSha256.ComputeHash(bytesToSign);
            string signature = Convert.ToBase64String(hashBytes);

            // add the signature information to the request headers
            request.Headers.Add(new AmazonHeader("AWSAccessKeyId", accessKeyId));
            request.Headers.Add(new AmazonHeader("Timestamp", timestamp));
            request.Headers.Add(new AmazonHeader("Signature", signature));

            return null;
        }

        public void AfterReceiveReply(ref Message reply, object correlationState) { }
    }
}
